/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./XMLSerializer.h"

/**************************************************************
* Constructors                                                *
***************************************************************/

    
/**************************************************************
* Operators                                                   *
***************************************************************/


/**************************************************************
* Other helpful functions                                     *
***************************************************************/
Point4D XMLSerializer::LoadPoint3D(TiXmlElement* pElem) {
	Point4D point;

	if (pElem) {
		if (!strcmp(pElem->Value(), "Point3D")) {
			pElem->QueryFloatAttribute("x", &point.mX);
			pElem->QueryFloatAttribute("y", &point.mY);
			pElem->QueryFloatAttribute("z", &point.mZ);
		}
	}

	return point;
}

Matrix4D XMLSerializer::LoadMatrix4D(TiXmlElement* pElem) {
	cFloat matTable[16];
	cChar buf[MAX_BUF_SIZE];
	cChar* retBuf = cNull;
	cChar* nextToken;

	if (pElem) {
		if (!strcmp(pElem->Value(), "WorldMatrix")) {
			strcpy_s(buf, pElem->GetText());
			retBuf = strtok_s(buf, " ", &nextToken);
			cInt iStartValue = 0;
			retBuf = strtok_s(buf, ";", &nextToken);
			if (retBuf) {
				matTable[0] = (cFloat) atof(retBuf);
			}
			iStartValue = 1;

			for (cInt i = iStartValue; i < 16; i++) {
				retBuf = strtok_s('\0', ";", &nextToken);
				if (retBuf) {
					matTable[i] = (cFloat) atof(retBuf);
				}
			}
			return Matrix4D(matTable);
		}
	}

	return Matrix4D();
}

BezierSurface* XMLSerializer::LoadBezierSurface(TiXmlElement* pElem) {
	TiXmlElement* pChild;
	BezierSurface* surface = new BezierSurface();
	cInt iRows = 0, iCols = 0, i = 0, j = 0;

	if (pElem) {
		if ((pChild = pElem->FirstChild()->ToElement())) {
			if (!strcmp(pChild->Value(), "Mesh")) {
				pChild->QueryIntAttribute("rows", &iRows);
				pChild->QueryIntAttribute("cols", &iCols);
				cBool bPeriodic = !strcmp(pChild->Attribute("closed"), "true") ? true : false;
				surface->Init(iRows, iCols);
				if ((pChild = pChild->FirstChild()->ToElement())) {
					while (pChild) {
						if ((i + 1) == iRows && (j + 1) == iCols) {
							surface->UpdateMeshPoint(i, j, LoadPoint3D(pChild));
						} else {
							surface->SetMeshPoint(i, j, LoadPoint3D(pChild));
						}
						i = !((j + 1) % iCols) ? ++i : i;
						j = ++j % iCols;
						pChild = pChild->NextSiblingElement();
					}
				}
				surface->SetPeriodic(bPeriodic);
			}
		}
	}
	return surface;
}

BSplineSurface* XMLSerializer::LoadBSplineSurface(TiXmlElement* pElem) {
	TiXmlElement* pChild;
	BSplineSurface* surface;
	cInt iRows = 0, iCols = 0, i = 0, j = 0, iU = 7, iV = 7;

	if (pElem) {
		if ((pChild = pElem->FirstChild()->ToElement())) {
			if (!strcmp(pChild->Value(), "Mesh")) {
				pElem->QueryIntAttribute("U", &iU);
				pElem->QueryIntAttribute("V", &iV);
				pChild->QueryIntAttribute("rows", &iRows);
				pChild->QueryIntAttribute("cols", &iCols);
				cBool bPeriodic = !strcmp(pChild->Attribute("closed"), "true") ? true : false;
				surface = new BSplineSurface(iU, iV);
				surface->Init(iRows, iCols);
				if ((pChild = pChild->FirstChild()->ToElement())) {
					while (pChild) {
						if ((i + 1) == iRows && (j + 1) == iCols) {
							surface->UpdateMeshPoint(i, j, LoadPoint3D(pChild));
						} else {
							surface->SetMeshPoint(i, j, LoadPoint3D(pChild));
						}
						i = !((j + 1) % iCols) ? ++i : i;
						j = ++j % iCols;
						pChild = pChild->NextSiblingElement();
					}
				}
				surface->SetPeriodic(bPeriodic);
				surface->SwitchControlPointsCoordinatesVisibility();
				surface->SwitchControlPointsVisibility();
				surface->SwitchMeshVisibility();
			}
		}
	}
	return surface;
}
cVoid XMLSerializer::IterateObjects() {
	mIterator = mLoadedObjects.begin();
}

Object3D* XMLSerializer::NextObject3D() {
	if (!mLoadedObjects.empty()) {
		if (mIterator != mLoadedObjects.end()) {
			Object3D* obj = *mIterator;
			mIterator++;
			return obj;
		}
	}
	mLoadedObjects.clear();
	mIterator = cNull;
	
	return cNull;
}

Matrix4D XMLSerializer::GetWorldMatrix() {
	return mWorldMatrix;
}

cBool XMLSerializer::SaveFile(const cChar* fileName, Scene3D* scene) {
	TiXmlDocument doc;  
	TiXmlElement* elem;

 	TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "Windows-1252", "");  
	doc.LinkEndChild(decl);  

	TiXmlElement* root = new TiXmlElement("Scene3D");  
	doc.LinkEndChild(root);  

	BSplineSurface* objSurf = cNull;
	Serializable* serObj = cNull;
	for (cInt i = 0; i < scene->GetNrOfObjects(); i++) {
		Object3D* obj = scene->GetObject(i + 3);
		if (obj) {	
			if ((objSurf = InstanceOf(BSplineSurface, obj))) {
				elem = objSurf->Serialize(root);
			} else if (InstanceOf(BezierSurface, obj)) {
				elem = ((BezierSurface*) obj)->Serialize(root);
			}
		}
	}
	elem = new TiXmlElement("WorldMatrix");
	scene->GetWorldMatrix().Serialize(elem);
	root->LinkEndChild(elem);

	return doc.SaveFile(fileName);
}

cBool XMLSerializer::LoadFile(const cChar* fileName) {
	TiXmlDocument doc(fileName);
	Object3D* obj = cNull;

	if (!doc.LoadFile()) {
		return false;
	}

	TiXmlHandle hDoc(&doc);
	TiXmlElement* pElem = cNull;
	TiXmlHandle hRoot(cNull);

	pElem = hDoc.FirstChildElement().Element();
	if (!pElem || strcmp(pElem->Value(), "Scene3D") != 0) {
		return false;
	}
	hRoot = TiXmlHandle(pElem);
	pElem = hRoot.FirstChild().Element();
	while (pElem) {
		const cChar* tagName = pElem->Value();
		if (!strcmp(tagName, "WorldMatrix")) {
			mWorldMatrix = LoadMatrix4D(pElem);
		} else if (!strcmp(tagName, "Object3D")) {
			if (!strcmp(pElem->Attribute("type"), "BSplineSurface")) {
				if ((obj = LoadBSplineSurface(pElem))) {
					mLoadedObjects.push_back(obj);
				}
			} else if (!strcmp(pElem->Attribute("type"), "BezierSurface")) {
				if ((obj = LoadBezierSurface(pElem))) {
					mLoadedObjects.push_back(obj);
				}
			}
		}
		pElem = pElem->NextSiblingElement();
	}

	return true;
}